feat: Containers gear (Phase 1) — Docker monitoring + agent skipTLSVerify + buildx push-dev - #37
Merged
Merged
Conversation
Implements the initial Containers gear across both gearbox-agent and gearbox dashboard, following the established gear architecture. Agent (gearbox-agent): - New gear at internal/gears/containers/ with Docker SDK (moby/moby/client) - Runtime detection (gracefully degrades when Docker is absent) - REST API: runtime info, container list, inspect, stats, logs, start/stop/restart/remove - Stack (Compose project) detection and grouping via container labels - Image listing endpoint - 21 passing tests covering all endpoints and helper functions Dashboard (gearbox): - New gear at internal/gears/containers/ with plugin.go, handlers.go, icons.go - Templ templates: containers page and settings - Sidebar entry at /containers with container icon (order 50) - Permission: containers:view / containers:manage - GearContainers constant and ContainersConfig added to database/gears.go - Default gear entry added (disabled by default) Frontend: - static/js/containers/containers-page.js — stacks view, all-containers view, search/filter, start/stop/restart actions, auto-refresh, confirm modal Closes #24 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NewClient and NewClientWithTimeout grew a skipTLSVerify bool parameter on this branch. The corresponding test files (client_test.go, functional_test.go) landed on main during the 2-month gap and used the old 2-arg signature, breaking the build after rebase. Pass false (the preserved default) at all call sites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Resurrects a 2-month-old branch that introduces a Phase-1 Containers (Docker) gear in both the dashboard and the agent, adds a per-box skip_tls_verify option (replacing the global GEARBOX_INSECURE_TLS env var), and adds docker-push-dev Makefile targets. A new RootHandler redirects / to the first enabled gear.
Changes:
- New
containersgear (agent collector + types + dashboard page/templates/JS/icons) plus a SQL migration andBoxDB.SkipTLSVerifyplumbed through everyagent.NewClientcall site and the HAProxy box UI/test flow. docker-push-devMakefile target in both modules.RootHandler/gearPathfor root-path navigation, withcontainersadded to the gear path map.
Reviewed changes
Copilot reviewed 36 out of 37 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| gearbox/static/js/containers/containers-page.js | New containers page JS; calls /api/v1/containers/* (no dashboard handlers exist). |
| gearbox/Makefile | Adds docker-push-dev (linux/amd64 only). |
| gearbox/internal/gears/containers/settings.templ | Stats interval settings UI with non-functional Save button. |
| gearbox/internal/gears/containers/plugin.go | Dashboard gear registration; only registers the page route. |
| gearbox/internal/gears/containers/pages.templ | Containers page layout. |
| gearbox/internal/gears/containers/icons.go | Sidebar SVG icon. |
| gearbox/internal/gears/containers/handlers.go | Containers page HTTP handler. |
| gearbox/internal/framework/templates/pages/haproxy_settings.templ | Adds Skip TLS Verify checkbox to box form. |
| gearbox/internal/framework/templates/layouts/base.templ | Adds containers to integration path map. |
| gearbox/internal/framework/models/server.go | SkipTLSVerify on BoxConfig. |
| gearbox/internal/framework/handler/root.go | New RootHandler and gearPath map. |
| gearbox/internal/framework/handler/haproxy_config.go | Propagates SkipTLSVerify; switches test probe from GetInfo to GetMetrics. |
| gearbox/internal/framework/handler/{gears,config,api_traffic,api_services,api_certificates}.go | Pass SkipTLSVerify to agent.NewClient. |
| gearbox/internal/framework/database/servers.go | Adds skip_tls_verify column to all box queries and ToBoxConfig. |
| gearbox/internal/framework/database/migrations/files/000001_add_skip_tls_verify_to_boxes.{up,down}.sql | New migration; down migration recreates table via CREATE TABLE … AS SELECT …. |
| gearbox/internal/framework/database/gears.go | Adds GearContainers constant, ContainersConfig, and default gear row. |
| gearbox/internal/framework/config/config.go | Removes GEARBOX_INSECURE_TLS env var. |
| gearbox/internal/framework/collector/{websocket_manager,registry}.go | Pass SkipTLSVerify to agent client. |
| gearbox/internal/framework/agent/client.go | NewClient(WithTimeout) gains skipTLSVerify arg; createTLSConfig is per-call. |
| gearbox/internal/framework/agent/{client,functional}_test.go | Test call-site updates for the new signature. |
| gearbox/cmd/server/main.go | Imports containers gear; registers RootHandler at /. |
| gearbox-agent/Makefile | Adds docker-push-dev. |
| gearbox-agent/cmd/gearbox-agent/main.go | Imports containers gear. |
| gearbox-agent/go.{mod,sum} | Adds moby/moby client + OpenTelemetry indirect deps. |
| gearbox-agent/internal/gears/containers/{types,plugin,plugin_test,detector,collector}.go | New agent-side Docker collector, runtime detector, HTTP handlers, and tests. |
Comments suppressed due to low confidence (1)
gearbox/static/js/containers/containers-page.js:279
stackActionandcontainerActionsimply show a "coming in Phase 3" / "coming soon" toast and never actually call the agent. The stack Up/Down/Restart buttons inbuildStackCardand the logs button inbuildContainerRoware wired up to these stubs, so the UI advertises functionality that is non-operational. Either disable/hide these buttons in Phase 1 or implement the dashboard→agent proxy so they work; shipping always-no-op buttons leads to a confusing user experience.
Comment on lines
+45
to
+47
| func (p *Gear) RegisterRoutes(r chi.Router) { | ||
| r.Get("/", p.handlers.ContainersPage) | ||
| } |
| @@ -0,0 +1,4 @@ | |||
| -- SQLite does not support DROP COLUMN in older versions; recreate table without skip_tls_verify | |||
| CREATE TABLE boxes_backup AS SELECT id, box_id, name, location, notes, agent_url, api_key_encrypted, enabled, auto_discovery, created_at, updated_at, created_by FROM boxes; | |||
Comment on lines
+113
to
+152
| card.innerHTML = ` | ||
| <div class="flex items-center justify-between px-4 py-3 cursor-pointer hover:bg-gray-50 dark:hover:bg-slate-750" | ||
| onclick="toggleStackExpand('${escHtml(stack.name)}')"> | ||
| <div class="flex items-center gap-3"> | ||
| <svg class="w-4 h-4 text-gray-400 dark:text-gray-500 stack-expand-icon transition-transform" | ||
| fill="none" stroke="currentColor" viewBox="0 0 24 24"> | ||
| <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/> | ||
| </svg> | ||
| <span class="font-semibold text-gray-900 dark:text-white">${escHtml(stack.name)}</span> | ||
| <span class="text-xs px-2 py-0.5 rounded-full font-medium ${statusColor}"> | ||
| ${running}/${total} running | ||
| </span> | ||
| </div> | ||
| <div class="flex items-center gap-2"> | ||
| <button onclick="event.stopPropagation(); stackAction('${escHtml(stack.name)}', 'up')" | ||
| class="px-2 py-1 text-xs bg-green-600 hover:bg-green-700 text-white rounded transition-colors">Up</button> | ||
| <button onclick="event.stopPropagation(); stackAction('${escHtml(stack.name)}', 'down')" | ||
| class="px-2 py-1 text-xs bg-red-600 hover:bg-red-700 text-white rounded transition-colors">Down</button> | ||
| <button onclick="event.stopPropagation(); stackAction('${escHtml(stack.name)}', 'restart')" | ||
| class="px-2 py-1 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded transition-colors">Restart</button> | ||
| </div> | ||
| </div> | ||
| <div class="stack-body hidden border-t border-gray-100 dark:border-slate-700 overflow-x-auto"> | ||
| <table class="w-full text-sm"> | ||
| <thead> | ||
| <tr class="bg-gray-50 dark:bg-slate-700/50 text-left"> | ||
| <th class="px-4 py-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Name</th> | ||
| <th class="px-4 py-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Image</th> | ||
| <th class="px-4 py-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Status</th> | ||
| <th class="px-4 py-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">CPU</th> | ||
| <th class="px-4 py-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Memory</th> | ||
| <th class="px-4 py-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Ports</th> | ||
| <th class="px-4 py-2 text-xs font-medium text-gray-500 dark:text-gray-400 uppercase">Actions</th> | ||
| </tr> | ||
| </thead> | ||
| <tbody id="stack-tbody-${escHtml(stack.name)}"></tbody> | ||
| </table> | ||
| </div>`; | ||
| renderContainerRows('stack-tbody-' + stack.name, stack.containers, false); | ||
| return card; |
Comment on lines
+100
to
+107
| @docker buildx build \ | ||
| --platform linux/amd64 \ | ||
| --build-arg VERSION=dev \ | ||
| --build-arg COMMIT_SHA=$(COMMIT_SHA) \ | ||
| --build-arg BUILD_DATE=$(BUILD_DATE) \ | ||
| -t $(IMAGE):dev \ | ||
| --push \ | ||
| . |
| // Step 2: Test authenticated endpoint | ||
| _, err = client.GetInfo() | ||
| // Step 2: Test authenticated endpoint — verifies the API key is valid | ||
| _, err = client.GetMetrics() |
Comment on lines
+32
to
+36
| <div class="mt-6"> | ||
| <button class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-lg transition-colors text-sm"> | ||
| Save Settings | ||
| </button> | ||
| </div> |
Resolve conflicts in:
- gearbox-agent/cmd/gearbox-agent/main.go — keep both `containers`
and `docker` gear imports (separate gears).
- gearbox-agent/go.mod + go.sum — union of dependency sets;
regenerated go.sum via `go mod tidy`. Adds Microsoft/go-winio,
UserExistsError/conpty, creack/pty, containerd, docker, etc. as
indirect deps for the two new gears.
- gearbox/cmd/server/main.go — comment merge, includes /containers.
- gearbox/internal/framework/database/servers.go — boxes table now
carries BOTH SkipTLSVerify (from this PR) and ConsoleEnabled
(from main). All SELECT / INSERT / UPDATE / Scan call sites
updated to handle both columns.
- gearbox/internal/framework/handler/haproxy_config.go — Create
and Update box handlers read both skip_tls_verify and
console_enabled form fields.
- gearbox/internal/framework/templates/pages/haproxy_settings.templ
— keep both the Skip-TLS checkbox and the Rotate-Key block.
- gearbox/internal/framework/database/migrations/files/ — renumber
PR 37's `000001_add_skip_tls_verify_to_boxes` migration to
`000004` so it doesn't collide with main's
`000001_add_failed_attempt_window`.
Propagate the SkipTLSVerify flag through the call sites that grew
since this PR was last rebased:
- agent.NewClient / NewClientWithTimeout / NewClientWithKID /
BuildTLSConfig now all take the skipTLSVerify flag.
- CapabilitiesCache.Get accepts skipTLSVerify and passes it through.
- Rotator.clientForKey threads it from BoxDB.SkipTLSVerify.
- Bx status check uses b.SkipTLSVerify.
- Console WS proxy uses server.SkipTLSVerify for BuildTLSConfig.
All `go build`, `go vet`, and `go test -count=1 ./...` pass clean in
both gearbox/ and gearbox-agent/ modules.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Resurrects the 2-month-old
feature/containers-gearbranch (last touched 2026-03-18), rebased onto current main. Bundles 4 themes:skipTLSVerifyoption — new bool onNewClient/NewClientWithTimeout, with a DB migration addingskip_tls_verifytoboxes, surfaced through HAProxy connection-test UI and box config.docker-push-devMakefile target — buildx, multi-platform, pushes:devdirectly to ghcr.io without going through GitHub Actions. Useful for fast feature-branch testing.RootHandler/gearPath— container navigation routing.What I had to fix to land the rebase
gearbox/internal/framework/database/gears.go— main addedGearHomeconst +HomeConfigstruct in the same regions where this branch addedGearContainersconst +ContainersConfigstruct. Kept both.gearbox-agent/go.sum— main and the branch both bumped many indirect deps (modernc/sqlite migration etc.). Regenerated withgo mod tidy.gearbox/Makefile—.PHONYline conflicted (main addeddeploy/deploy-build/deploy-restart, branch addeddocker-push-dev). Combined.client_test.go/functional_test.go— these tests landed on main during the 2-month gap; they callNewClientwith the old 2-arg signature. This branch's commite275b19added a 3rdskipTLSVerifyarg. Updated all 39 call sites to passfalse(the preserved default behavior). One commit on top of the rebase:672ed34.One commit (
ef087f8 fix: update toggle function name in gearCard template) was dropped bygit rebaseas already-upstream — that fix landed via another path.Test plan
CGO_ENABLED=0 go build ./...passes in bothgearbox/andgearbox-agent/CGO_ENABLED=0 go test ./...passes in both moduleslinux/amd64andlinux/arm64Why Draft
Putting up as Draft because:
skipTLSVerifyUX might benefit from a security review (InsecureSkipVerifyis rarely the right default for new TLS clients).🤖 Generated with Claude Code